package com.zsc.boot.job;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zsc.boot.bean.User;
import com.zsc.boot.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class PreRedisJob {
    @Resource
    private UserService userService;
    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    @Resource
    private RedissonClient redissonClient;

    //重点用户
    private List<Long> userList = Arrays.asList(1L);//id为1的用户

    /**
     * 缓存预热（每天执行次定时任务，预热推荐用户页面）
     */
    @Scheduled(cron = "0 50 0,15 * * *")
    public void PreCacheJob(){
        RLock lock = redissonClient.getLock("partner:precachejob:docache:lock");//给锁起一个名字，这里命名的方式与redisKey一致
        //如果抢到锁了就执行定时任务，没有抢到就直接返回
        try {
            if(lock.tryLock(0,30000L,TimeUnit.MILLISECONDS)){//第一个参数为等待时间，第二参数为过期时间
                for (Long userId : userList) {
                    //因为公司中为了减少成本，会将多个项目用同一个redis，所以避免key值重复必须自定义
                    String redisKey = String.format("partner:user:recommend:%s",userId);

                    ValueOperations<String,Object> valueOperations = redisTemplate.opsForValue();
                    //无缓存查数据库
                    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
                    //进行分页操作需要执行每页的大小和当前是第几页
                    Page<User> userPage = userService.page(new Page<>(1,20), queryWrapper);

                    //写入缓存,将数据库中查出的userPage直接放入缓存
                    try { //这里之所以用try - catch 进行捕获，是因为即使redis写入出错了，仍能查数据库返回给前端
                        valueOperations.set(redisKey,userPage,1, TimeUnit.MINUTES);
                    } catch (Exception e) {
                        log.error("redis set key error!",e);
                    }
                }
            }
        } catch (InterruptedException e) {
            log.error("PreCacheJob error");
        }finally {
            //注意：锁的释放一定要在finally中，如果上面try的代码出异常不会释放锁，finally的代码一定执行
            lock.unlock();
        }
    }
}
